﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Data.SqlClient;
using System.Configuration;
using System.Web.WebPages.Html;

/// <summary>
/// Determina la estructura basica de un DAO
/// </summary>
public class DaoGenerico<T>
{
    private SqlConnection conexion;

    /// <summary>
    /// Crea una nueva instancia de la clase
    /// </summary>
    public DaoGenerico() { }

    /// <summary>
    /// Crea un nuevo registro de la entidad
    /// </summary>
    /// <param name="entidad">Informaci&oacute;n de la entidad</param>
    public void crear(T entidad)
    {
        StringBuilder sBuilder = new StringBuilder("INSERT INTO ");
        StringBuilder sqlInsert = new StringBuilder(") VALUES (");
        List<SqlParameter> parametros = new List<SqlParameter>();

        Type tipo = entidad.GetType();

        sBuilder.Append(tipo.Name)
            .Append(" (");

        foreach (PropertyInfo propiedad in tipo.GetProperties())
        {
            //
            if (propiedad.Name.ToLower().IndexOf("id_" + tipo.Name) == -1)
            {
                string nombreCampo = propiedad.Name;
                parametros.Add(new SqlParameter("@" + nombreCampo, propiedad.GetValue(entidad, null)));

                sBuilder.Append(nombreCampo).Append(", ");
                sqlInsert.Append("@").Append(nombreCampo).Append(", ");
            }
        }
        string sql = sqlInsert.Insert(0, sBuilder.ToString(0, sBuilder.Length - 2)).ToString(0, sqlInsert.Length - 2) + ");";
        ejecutarSinConsulta(sql, parametros);
    }

    /// <summary>
    /// Actualiza un registro de la entidad
    /// </summary>
    /// <param name="entidad">Informaci&oacute; de la entidad</param>
    public void actualizar(T entidad)
    {
        StringBuilder sBuilder = new StringBuilder("UPDATE ");
        StringBuilder sqlWhere = new StringBuilder(" WHERE ");
        List<SqlParameter> parametros = new List<SqlParameter>();

        Type tipo = entidad.GetType();

        sBuilder.Append(tipo.Name)
            .Append(" SET ");

        foreach (PropertyInfo propiedad in tipo.GetProperties())
        {
            string nombreCampo = propiedad.Name;
            object valor = propiedad.GetValue(entidad, null);

            parametros.Add(new SqlParameter("@" + nombreCampo, valor));

            
            //if (nombreCampo.IndexOf("id_") == 0)
            if (propiedad.Name.ToLower().IndexOf("id_" + tipo.Name) == 0)                
            {
                sqlWhere.Append(nombreCampo).Append(" = @").Append(nombreCampo);
            }
            else if (valor != null)
            {
                sBuilder.Append(nombreCampo).Append(" = @").Append(nombreCampo).Append(", ");
            }
        }
        string sql = sqlWhere.Insert(0, sBuilder.ToString(0, sBuilder.Length - 2)) + ";";
        ejecutarSinConsulta(sql, parametros);
    }

    /// <summary>
    /// Elimina un registro de la entidad
    /// </summary>
    /// <param name="entidad">Informaci&oacute; de la entidad a eliminar</param>
    public void eliminar(T entidad)
    {
        StringBuilder sqlDelete = new StringBuilder("DELETE FROM ");
        List<SqlParameter> parametros = new List<SqlParameter>();

        Type tipo = entidad.GetType();

        sqlDelete.Append(tipo.Name)
            .Append(" WHERE ");

        foreach (PropertyInfo propiedad in tipo.GetProperties())
        {
            if (propiedad.Name.IndexOf("id_") == 0)
            {
                string nombreCampo = propiedad.Name;
                parametros.Add(new SqlParameter("@" + nombreCampo, propiedad.GetValue(entidad, null)));
                sqlDelete.Append(nombreCampo).Append(" = @").Append(nombreCampo);

                break;
            }
        }
        sqlDelete.Append(";");
        ejecutarSinConsulta(sqlDelete.ToString(), parametros);
    }

    /// <summary>
    /// Lista un listado de datos segun la informaci&oacute;n de la entidad
    /// </summary>
    /// <returns>Listado de registros pertenecientes a la entidad</returns>
    /// <param name="entidad">Informaci&oacute; de la entidad para realizar el filtro de la informacion</param>
    public List<T> filtrar(T entidad)
    {
        List<T> resultado = null;
        StringBuilder sqlSelect = new StringBuilder("SELECT * FROM ");
        StringBuilder sqlWhere = new StringBuilder(" WHERE ");
        List<SqlParameter> parametros = new List<SqlParameter>();

        Type tipo = entidad.GetType();

        sqlSelect.Append(tipo.Name);

        //Construir la clausula where de la sentencia
        foreach (PropertyInfo propiedad in tipo.GetProperties())
        {
            string nombreCampo = propiedad.Name;
            object valor = propiedad.GetValue(entidad, null);

            if (valor != null)
            {
                if (valor.ToString().Replace("0", "") != string.Empty)
                {

                    if (valor.GetType().Equals(typeof(DateTime)))
                    {
                        DateTime fecha;
                        fecha = Convert.ToDateTime(valor);

                        if (fecha > DateTime.MinValue)
                        {
                            parametros.Add(new SqlParameter("@" + nombreCampo, valor));
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else {
                        parametros.Add(new SqlParameter("@" + nombreCampo, valor));
                    }

                    if (sqlWhere.ToString().Trim() == "WHERE")
                    {
                        sqlWhere.Append(nombreCampo).Append(" = @").Append(nombreCampo);
                    }
                    else
                    {
                        if (valor.GetType().Equals(typeof(DateTime)))
                        {
                            DateTime fecha;
                            fecha = Convert.ToDateTime(valor);

                            if (fecha > DateTime.MinValue)
                            {
                                sqlWhere.Append(" AND ").Append(nombreCampo).Append(" = @").Append(nombreCampo);
                            }
                        }
                        else
                        {
                            sqlWhere.Append(" AND ").Append(nombreCampo).Append(" = @").Append(nombreCampo);
                        }

                    }
                }
            }
        }

        if (parametros.Capacity > 0)
        {
            sqlSelect.Append(sqlWhere.ToString());
        }
        sqlSelect.Append(";");

        string sql = sqlSelect.ToString();
        resultado = ejecutarConsulta(sql, parametros);

        return resultado;
    }

    /// <summary>
    /// Lista toda la informaci&oacute;n de la tabla
    /// </summary>
    /// <returns>Listado de registros pertenecientes a la entidad</returns>
    /// <param name="entidad">Informaci&oacute; de la entidad</param>
    public List<T> listar(T entidad)
    {
        List<T> resultado = null;
        StringBuilder sqlSelect = new StringBuilder("SELECT * FROM ");
        
        List<SqlParameter> parametros = new List<SqlParameter>();

        Type tipo = entidad.GetType();

        sqlSelect.Append(tipo.Name);
        sqlSelect.Append(";");

        resultado = ejecutarConsulta(sqlSelect.ToString(), null);

        return resultado;
    }

    /// <summary>
    /// Metodo realiza la conexion a la base de datos y ejecuta la sentencia Transact-SQL(UPDATE, DELETE, CREATE)
    /// </summary>
    /// <param name="sql">Sentecia SQL a ejecutar</param>
    /// <param name="parametros">Parametros para la ejecución del query</param>
    protected void ejecutarSinConsulta(string sql, List<SqlParameter> parametros)
    {
        SqlConnection conexion = new SqlConnection(ConfigurationManager.ConnectionStrings["web-vote"].ConnectionString);
        SqlCommand comando = new SqlCommand();

        try
        {
            //Abre la conexion a la base de datos
            conexion.Open();

            comando.Connection = conexion;
            comando.CommandText = sql;
            comando.Parameters.AddRange(parametros.ToArray());

            //Se ejecuta la sentencia de actualizacion 
            comando.ExecuteNonQuery();
        }
        catch (Exception ex)
        {
            throw new DaoException("Ha ocurrido un error en la ejecución de la operacion.");
        }
        finally
        {
            conexion.Close();
            comando.Dispose();
        }
    }

    /// <summary>
    /// Metodo realiza la conexion a la base de datos y ejecuta la sentencia SQL de consulta (SELECT)
    /// </summary>
    /// <param name="sql">Sentecia SQL a ejecutar</param>
    /// <param name="parametros">Parametros para la ejecución del query</param>
    protected List<T> ejecutarConsulta(string sql, List<SqlParameter> parametros)
    {
        List<T> resultado = new List<T>();
        SqlConnection conexion = new SqlConnection(ConfigurationManager.ConnectionStrings["web-vote"].ConnectionString);
        SqlCommand comando = new SqlCommand();
        SqlDataReader reader = null;

        try
        {
            //Abre la conexion a la base de datos
            conexion.Open();

            comando.Connection = conexion;
            if (sql.Contains("Sp"))
            {
                comando.CommandType = System.Data.CommandType.StoredProcedure;
            }

            comando.CommandText = sql;

            if (parametros != null)
            {
                comando.Parameters.AddRange(parametros.ToArray());
            }

            //Se ejecuta la sentencia de actualizacion 
            reader = comando.ExecuteReader();

            while (reader.Read())
            {
                //Se instancia dinamicamente la clase del tipo especificado
                T entidad = Activator.CreateInstance<T>();

                //Se obtienen las propiedades de la clase y se recorren
                foreach (PropertyInfo propiedad in entidad.GetType().GetProperties())
                {
                    //Se obtiene la posicion de la propiedad en el resultado
                    if (ValidarColumna(reader, propiedad.Name.Replace("_vo", "")))
                    {
                        int i = reader.GetOrdinal(propiedad.Name.Replace("_vo", ""));

                        propiedad.SetValue(entidad, reader.GetValue(i), null);
                    }
                }
                resultado.Add(entidad);
            }
        }
        catch (Exception ex)
        {
            throw new DaoException("Ha ocurrido un error en la ejecución de la operacion.");
        }
        finally
        {
            if (reader != null)
            {
                reader.Close();
            }
            comando.Dispose();
            conexion.Close();
        }
        return resultado;
    }

    /// <summary>
    /// ejecuta sp
    /// </summary>
    /// <param name="sql"></param>
    /// <param name="entidad"></param>
    /// <param name="valida_where"></param>
    /// <returns></returns>
    public List<T> listar_sp(string sql, T entidad, int valida_where)
    {
        List<T> resultado = new List<T>();
        List<SqlParameter> parametros = new List<SqlParameter>();
        Type tipo = entidad.GetType();
        //parametros.Add(new SqlParameter("@" + nombreCampo, valor));
        foreach (PropertyInfo propiedad in tipo.GetProperties())
        {
            string nombreCampo = propiedad.Name;
            object valor = propiedad.GetValue(entidad, null);
            if (valor != null && valida_where == 1)
            {
                if (valor.ToString().Replace("0", "") != string.Empty && !valor.ToString().Contains("01/01/0001"))
                {
                    parametros.Add(new SqlParameter("@" + nombreCampo.Replace("_vo", ""), valor));
                }
            }
        }

        resultado = ejecutarConsulta(sql, parametros);
        return resultado;
    }

    public bool ValidarColumna(SqlDataReader reader, string nombreColumna)
    {
        reader.GetSchemaTable().DefaultView.RowFilter = "ColumnName= '" + nombreColumna + "'";
        return (reader.GetSchemaTable().DefaultView.Count > 0);
    }

}